/**
 * 
 */
package geometry;

import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import util.Constants;

/**
 * This models the application space, i.e. the space where the moving objects
 * could possibly move.
 * 
 * @author wu wei
 * 
 */
public class Space implements Constants {

	double xmin, ymin; // defines left corner of the space

	double spaceWidth, spaceHeight;

	ArrayList<LineSegment> borders;

	public Space(double xmin, double ymin, double width, double height) {
		this.xmin = xmin;
		this.ymin = ymin;
		this.spaceWidth = width;
		this.spaceHeight = height;
		this.borders = new ArrayList<LineSegment>();
		this.addBorders();
	}

	private void addBorders() {
		Point2D.Double topLeft = new Point2D.Double(this.xmin, this.ymin
				+ this.spaceHeight);
		Point2D.Double topRight = new Point2D.Double(this.xmin
				+ this.spaceWidth, this.ymin + this.spaceHeight);
		Point2D.Double downLeft = new Point2D.Double(this.xmin, this.ymin);
		Point2D.Double downRight = new Point2D.Double(this.xmin
				+ this.spaceWidth, this.ymin);
		LineSegment top = new LineSegment(topLeft, topRight);
		LineSegment down = new LineSegment(downLeft, downRight);
		LineSegment left = new LineSegment(topLeft, downLeft);
		LineSegment right = new LineSegment(topRight, downRight);
		this.borders.add(top);
		this.borders.add(down);
		this.borders.add(left);
		this.borders.add(right);
	}

	public Collection<LeveledIntersection> getSpaceCorners() {
		ArrayList<LeveledIntersection> corners = new ArrayList<LeveledIntersection>();
		LineSegment top = this.borders.get(0);
		LineSegment down = this.borders.get(1);
		LineSegment left = this.borders.get(2);
		LineSegment right = this.borders.get(3);
		corners.add(new LeveledIntersection(top, left));
		corners.add(new LeveledIntersection(top, right));
		corners.add(new LeveledIntersection(down, left));
		corners.add(new LeveledIntersection(down, right));
		return corners;
	}

	public Collection<Line2D.Double> getSpaceBorders() {
		Collection<Line2D.Double> bds = new ArrayList<Line2D.Double>();
		for (LineSegment ls : this.borders) {
			bds.add(ls.asLine2DDouble());
		}
		return bds;
	}

	public Collection<LineSegment> getBorderSegments() {
		return this.borders;
	}

	/**
	 * computes the given bisector's intersections with space borders.
	 * 
	 * @param bi
	 * @return
	 */
	public List<LeveledIntersection> getIntersectionsWithBorders(LineFunction bi) {
		ArrayList<LeveledIntersection> wipsWithBorders = new ArrayList<LeveledIntersection>();
		for (LineSegment border : this.borders) {
			if (border.doesIntersect(bi)) {
				LeveledIntersection wip = new LeveledIntersection(border, bi);
				if (this.inSpace(wip)) {
					wipsWithBorders.add(wip);
				}
			}
		}
		assert (wipsWithBorders.size() == 2) : "More than two intersections with borders are found!";
		return wipsWithBorders;
	}

	public boolean inSpace(Point2D.Double point) {
		double xmax = this.xmin + this.spaceWidth;
		double ymax = this.ymin + this.spaceHeight;
		if (point.x >= xmin - tinyVal && point.x <= xmax + tinyVal
				&& point.y >= ymin - tinyVal && point.y <= ymax + tinyVal) {
			return true;
		}
		return false;
	}

	public double getMinX() {
		return xmin;
	}

	public double getMaxX() {
		return xmin + this.spaceWidth;
	}

	public double getMinY() {
		return ymin;
	}

	public double getMaxY() {
		return ymin + this.spaceHeight;
	}

	/**
	 * @return a rectangle representation of this space.
	 */
	public Rectangle2D.Double asRect() {
		return new Rectangle2D.Double(this.xmin, this.ymin, this.spaceWidth,
				this.spaceHeight);
	}

	/**
	 * generate a line segment on the bisector that is a superset of the given
	 * line's part in the space.
	 * 
	 * @param lf
	 * @return
	 */
	public Line2D.Double getLongCrossLine(LineFunction lf) {
		double x1 = 0;
		double y1 = 0;
		double x2 = 0;
		double y2 = 0;
		switch (lf.getLineType()) {
		case XPARALLEL:
			y1 = lf.getARandomPointOnTheLine().getY();
			y2 = y1;
			x1 = this.getMinX() - 1;
			x2 = this.getMaxX() + 1;
			break;
		case YPARALLEL:
			x1 = lf.getARandomPointOnTheLine().getX();
			x2 = x1;
			y1 = this.getMinY() - 1;
			y2 = this.getMaxY() + 1;
			break;
		case NORMAL:
			x1 = this.getMinX() - 1;
			x2 = this.getMaxX() + 1;
			y1 = lf.getYByX(x1);
			y2 = lf.getYByX(x2);
			break;
		}
		return new Line2D.Double(new Point2D.Double(x1, y1),
				new Point2D.Double(x2, y2));
	}

	/**
	 * generate a line segment on the bisector that is the given line's part in
	 * the space.
	 * 
	 * @param lf
	 * @return
	 */
	public Line2D.Double getCrossLine(LineFunction lf) {
		List<LeveledIntersection> ints = this.getIntersectionsWithBorders(lf);
		LeveledIntersection int1 = ints.get(0);
		Point2D.Double p1 = new Point2D.Double(int1.x, int1.y);
		LeveledIntersection int2 = ints.get(1);
		Point2D.Double p2 = new Point2D.Double(int2.x, int2.y);
		return new Line2D.Double(p1, p2);
	}

	public String toString() {
		String s = "(";
		s += this.xmin + ", ";
		s += this.ymin + ", ";
		s += this.spaceWidth + ", ";
		s += this.spaceHeight + ")";
		return s;
	}

}
